using Microsoft.Ajax.Utilities;
using Newtonsoft.Json;
using Nop.Admin.Models.Common;
using Nop.Admin.Models.Customers;
using Nop.Admin.Models.ShoppingCart;
using Nop.Core;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Forums;
using Nop.Core.Domain.Logistics;
using Nop.Core.Domain.Messages;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Tax;
using Nop.Services.Authentication.External;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.ExportImport;
using Nop.Services.Forums;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Security;
using Nop.Services.Stores;
using Nop.Services.Tax;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.ModelBinding;
using System.Web.Mvc;
using Telerik.Web.Mvc;


namespace Nop.Admin.Controllers
{
    [AdminAuthorize]
    public partial class CustomerController : BaseNopController
    {
        #region Fields

        private readonly ICustomerService _customerService;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly ICustomerRegistrationService _customerRegistrationService;
        private readonly ICustomerReportService _customerReportService;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly ILocalizationService _localizationService;
        private readonly DateTimeSettings _dateTimeSettings;
        private readonly TaxSettings _taxSettings;
        private readonly RewardPointsSettings _rewardPointsSettings;
        private readonly ICountryService _countryService;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly IAddressService _addressService;
        private readonly CustomerSettings _customerSettings;
        private readonly ITaxService _taxService;
        private readonly IWorkContext _workContext;
        private readonly IPriceFormatter _priceFormatter;
        private readonly IOrderService _orderService;
        private readonly IExportManager _exportManager;
        private readonly IImportManager _importManager;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly IPermissionService _permissionService;
        private readonly AdminAreaSettings _adminAreaSettings;
        private readonly IQueuedEmailService _queuedEmailService;
        private readonly EmailAccountSettings _emailAccountSettings;
        private readonly IEmailAccountService _emailAccountService;
        private readonly ForumSettings _forumSettings;
        private readonly IForumService _forumService;
        private readonly IOpenAuthenticationService _openAuthenticationService;
        private readonly AddressSettings _addressSettings;
        private readonly IStoreService _storeService;
        private readonly ILogisticsService _logisticsService;
        private readonly Nop.Services.Logistics.IOrderProcessingService _orderProcessingService;
        private readonly IInvoiceService _invoiceService;
        private readonly IPdfService _pdfService;
        private readonly IMeasureService _measureService;
        private readonly MeasureSettings _measureSettings;
        private readonly LogisticsInformationSettings _logisticsInformationSettings;

        #endregion

        #region Constructors

        public CustomerController(ICustomerService customerService,
            IGenericAttributeService genericAttributeService,
            ICustomerRegistrationService customerRegistrationService,
            ICustomerReportService customerReportService, IDateTimeHelper dateTimeHelper,
            ILocalizationService localizationService, DateTimeSettings dateTimeSettings,
            TaxSettings taxSettings, RewardPointsSettings rewardPointsSettings,
            ICountryService countryService, IStateProvinceService stateProvinceService,
            IAddressService addressService,
            CustomerSettings customerSettings, ITaxService taxService,
            IWorkContext workContext, IPriceFormatter priceFormatter,
            IOrderService orderService, IExportManager exportManager,
            IMeasureService measureService,
            MeasureSettings measureSettings, LogisticsInformationSettings logisticsInformationSettings,
            Nop.Services.Logistics.IOrderProcessingService orderProcessingService,
            ICustomerActivityService customerActivityService,
            IPriceCalculationService priceCalculationService,
            IPermissionService permissionService, AdminAreaSettings adminAreaSettings,
            IQueuedEmailService queuedEmailService, EmailAccountSettings emailAccountSettings,
            IEmailAccountService emailAccountService, ForumSettings forumSettings,
            IForumService forumService, IOpenAuthenticationService openAuthenticationService,
            AddressSettings addressSettings,
            IStoreService storeService,
            ILogisticsService logisticsService,
            IInvoiceService invoiceService,
            IPdfService pdfService,
            IImportManager importManager)
        {
            this._customerService = customerService;
            this._genericAttributeService = genericAttributeService;
            this._customerRegistrationService = customerRegistrationService;
            this._customerReportService = customerReportService;
            this._dateTimeHelper = dateTimeHelper;
            this._localizationService = localizationService;
            this._dateTimeSettings = dateTimeSettings;
            this._taxSettings = taxSettings;
            this._rewardPointsSettings = rewardPointsSettings;
            this._countryService = countryService;
            this._stateProvinceService = stateProvinceService;
            this._addressService = addressService;
            this._customerSettings = customerSettings;
            this._taxService = taxService;
            this._workContext = workContext;
            this._priceFormatter = priceFormatter;
            this._orderService = orderService;
            this._exportManager = exportManager;
            this._measureService = measureService;
            this._measureSettings = measureSettings;
            this._logisticsInformationSettings = logisticsInformationSettings;
            this._customerActivityService = customerActivityService;
            this._priceCalculationService = priceCalculationService;
            this._permissionService = permissionService;
            this._adminAreaSettings = adminAreaSettings;
            this._queuedEmailService = queuedEmailService;
            this._emailAccountSettings = emailAccountSettings;
            this._emailAccountService = emailAccountService;
            this._forumSettings = forumSettings;
            this._forumService = forumService;
            this._openAuthenticationService = openAuthenticationService;
            this._addressSettings = addressSettings;
            this._storeService = storeService;
            this._logisticsService = logisticsService;
            this._orderProcessingService = orderProcessingService;
            this._invoiceService = invoiceService;
            this._pdfService = pdfService;
            this._importManager = importManager;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected bool IsInterDistrict(Address address1, Address address2)
        {
            var district1 = _logisticsService.GetDistrictByZipPostalCode(address1.ZipPostalCode);
            var district2 = _logisticsService.GetDistrictByZipPostalCode(address2.ZipPostalCode);

            return (district1 != null && district2 != null && district1.Id != district2.Id);
        }

        [NonAction]
        protected string GetCustomerRolesNames(IList<CustomerRole> customerRoles, string separator = ",")
        {
            var sb = new StringBuilder();
            for (int i = 0; i < customerRoles.Count; i++)
            {
                sb.Append(customerRoles[i].Name);
                if (i != customerRoles.Count - 1)
                {
                    sb.Append(separator);
                    sb.Append(" ");
                }
            }
            return sb.ToString();
        }

        [NonAction]
        protected IList<RegisteredCustomerReportLineModel> GetReportRegisteredCustomersModel()
        {
            var report = new List<RegisteredCustomerReportLineModel>();
            report.Add(new RegisteredCustomerReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Customers.Reports.RegisteredCustomers.Fields.Period.7days"),
                Customers = _customerReportService.GetRegisteredCustomersReport(7)
            });

            report.Add(new RegisteredCustomerReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Customers.Reports.RegisteredCustomers.Fields.Period.14days"),
                Customers = _customerReportService.GetRegisteredCustomersReport(14)
            });
            report.Add(new RegisteredCustomerReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Customers.Reports.RegisteredCustomers.Fields.Period.month"),
                Customers = _customerReportService.GetRegisteredCustomersReport(30)
            });
            report.Add(new RegisteredCustomerReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Customers.Reports.RegisteredCustomers.Fields.Period.year"),
                Customers = _customerReportService.GetRegisteredCustomersReport(365)
            });

            return report;
        }

        [NonAction]
        protected IList<CustomerModel.AssociatedExternalAuthModel> GetAssociatedExternalAuthRecords(Customer customer)
        {
            if (customer == null)
                throw new ArgumentNullException("customer");

            var result = new List<CustomerModel.AssociatedExternalAuthModel>();
            foreach (var record in _openAuthenticationService.GetExternalIdentifiersFor(customer))
            {
                var method = _openAuthenticationService.LoadExternalAuthenticationMethodBySystemName(record.ProviderSystemName);
                if (method == null)
                    continue;

                result.Add(new CustomerModel.AssociatedExternalAuthModel()
                {
                    Id = record.Id,
                    Email = record.Email,
                    ExternalIdentifier = record.ExternalIdentifier,
                    AuthMethodName = method.PluginDescriptor.FriendlyName
                });
            }

            return result;
        }

        [NonAction]
        protected CustomerModel PrepareCustomerModelForList(Customer customer)
        {
            var model = new CustomerModel()
            {
                Id = customer.Id,
                Email = !String.IsNullOrEmpty(customer.Email) ? customer.Email : (customer.IsGuest() ? _localizationService.GetResource("Admin.Customers.Guest") : "Unknown"),
                Username = customer.Username,
                FullName = customer.GetFullName(),
                Company = customer.GetAttribute<string>(SystemCustomerAttributeNames.Company),
                Phone = customer.GetAttribute<string>(SystemCustomerAttributeNames.Phone),
                CellPhone = customer.GetAttribute<string>(SystemCustomerAttributeNames.CellPhone),
                ZipPostalCode = customer.GetAttribute<string>(SystemCustomerAttributeNames.ZipPostalCode),
                CustomerRoleNames = GetCustomerRolesNames(customer.CustomerRoleMappings.Select(crm => crm.CustomerRole).ToList()),
                Active = customer.Active,
                CreatedOn = _dateTimeHelper.ConvertToUserTime(customer.CreatedOnUtc, DateTimeKind.Utc),
                LastActivityDate = _dateTimeHelper.ConvertToUserTime(customer.LastActivityDateUtc, DateTimeKind.Utc),
            };

            model.CreatedOnStr = model.CreatedOn.ToString("G");
            model.LastActivityDateStr = model.LastActivityDate.ToString("G");

            return model;
        }

        [NonAction]
        protected string ValidateCustomerRoles(IList<CustomerRole> customerRoles)
        {
            if (customerRoles == null)
                throw new ArgumentNullException("customerRoles");

            //ensure a customer is not added to both 'Guests' and 'Registered' customer roles
            //ensure that a customer is in at least one required role ('Guests' and 'Registered')
            bool isInGuestsRole = customerRoles
                .Where(cr => cr.SystemName == SystemCustomerRoleNames.Guests)
                .FirstOrDefault() != null;
            bool isInRegisteredRole =
                customerRoles
                .Where(cr => cr.SystemName == SystemCustomerRoleNames.Registered)
                .FirstOrDefault() != null;
            if (isInGuestsRole && isInRegisteredRole)
                return "The customer cannot be in both 'Guests' and 'Registered' customer roles";
            if (!isInGuestsRole && !isInRegisteredRole)
                return "Add the customer to 'Guests' or 'Registered' customer role";

            //no errors
            return "";
        }

        [NonAction]
        protected int GetExpressMemberExpireDays(Customer customer)
        {
            var expireDate = customer.GetAttribute<DateTime?>(
                SystemCustomerAttributeNames.LogisticsMemberExpireDateTimeOnUtc, _genericAttributeService);
            int days = 0;
            if (expireDate.HasValue)
            {
                if (expireDate <= DateTime.UtcNow)
                {
                    _genericAttributeService.SaveAttribute<DateTime?>(customer, SystemCustomerAttributeNames.LogisticsMemberExpireDateTimeOnUtc, null);
                    return 0;
                }
                else
                {
                    days = (expireDate.Value - DateTime.UtcNow).Days;
                    if (days < 0)
                        days = 0;
                    else
                    {
                        days += 1;
                    }
                }
            }
            return days;
        }

        [NonAction]
        protected void SetExpressMemberExpireDays(Customer customer, int days)
        {
            if (days <= 0)
            {
                _genericAttributeService.SaveAttribute<DateTime?>(customer, SystemCustomerAttributeNames.LogisticsMemberExpireDateTimeOnUtc, null);
            }
            else
            {
                DateTime? expireDate = DateTime.UtcNow.AddDays(days);
                _genericAttributeService.SaveAttribute<DateTime?>(customer, SystemCustomerAttributeNames.LogisticsMemberExpireDateTimeOnUtc, expireDate);
            }
        }

        #endregion

        #region Customers

        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        public ActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            //load registered customers by default
            var defaultRoleIds = new[] { _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered).Id };
            var listModel = new CustomerListModel()
            {
                UsernamesEnabled = _customerSettings.UsernamesEnabled,
                DateOfBirthEnabled = _customerSettings.DateOfBirthEnabled,
                CompanyEnabled = _customerSettings.CompanyEnabled,
                PhoneEnabled = _customerSettings.PhoneEnabled,
                CellPhoneEnabled = _customerSettings.CellPhoneEnabled,
                ZipPostalCodeEnabled = _customerSettings.ZipPostalCodeEnabled,
                AvailableCustomerRoles = _customerService.GetAllCustomerRoles(true).Select(cr => cr.ToModel()).ToList(),
                SearchCustomerRoleIds = defaultRoleIds,
            };

            listModel.RoleSearchRestriction = false;
            var customers = _customerService.GetAllCustomers(null, null, defaultRoleIds, listModel.RoleSearchRestriction, null,
                null, null, null, 0, 0, null, null, null, null,
                false, null, 0, _adminAreaSettings.GridPageSize);
            //customer list
            listModel.Customers = new GridModel<CustomerModel>
            {
                Data = customers.Select(PrepareCustomerModelForList),
                Total = customers.TotalCount
            };
            return View(listModel);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult CustomerList(GridCommand command, CustomerListModel model,
            [ModelBinderAttribute(typeof(CommaSeparatedModelBinder))] int[] searchCustomerRoleIds)
        {
            //we use own own binder for searchCustomerRoleIds property 
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            DateTime? registrationFromValue = (model.RegistrationFrom == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.RegistrationFrom.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? registrationToValue = (model.RegistrationTo == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.RegistrationTo.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            var searchDayOfBirth = 0;
            int searchMonthOfBirth = 0;
            if (!String.IsNullOrWhiteSpace(model.SearchDayOfBirth))
                searchDayOfBirth = Convert.ToInt32(model.SearchDayOfBirth);
            if (!String.IsNullOrWhiteSpace(model.SearchMonthOfBirth))
                searchMonthOfBirth = Convert.ToInt32(model.SearchMonthOfBirth);

            var customers = _customerService.GetAllCustomers(registrationFromValue, registrationToValue,
                searchCustomerRoleIds, model.RoleSearchRestriction, model.SearchEmail, model.SearchUsername,
                model.SearchFirstName, model.SearchLastName,
                searchDayOfBirth, searchMonthOfBirth,
                model.SearchCompany, model.SearchPhone, model.SearchCellPhone, model.SearchZipPostalCode,
                false, null, command.Page - 1, command.PageSize);
            var gridModel = new GridModel<CustomerModel>
            {
                Data = customers.Select(PrepareCustomerModelForList),
                Total = customers.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult Create()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var model = new CustomerModel();
            model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            model.AllowUsersToChangeUsernames = _customerSettings.AllowUsersToChangeUsernames;
            model.AllowCustomersToSetTimeZone = _dateTimeSettings.AllowCustomersToSetTimeZone;
            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == _dateTimeHelper.DefaultStoreTimeZone.Id) });
            model.DisplayVatNumber = false;
            //customer roles
            model.AvailableCustomerRoles = _customerService
                .GetAllCustomerRoles(true)
                .Select(cr => cr.ToModel())
                .ToList();
            model.SelectedCustomerRoleIds = new int[0];
            model.AllowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles);
            //form fields
            model.GenderEnabled = _customerSettings.GenderEnabled;
            model.DateOfBirthEnabled = _customerSettings.DateOfBirthEnabled;
            model.CompanyEnabled = _customerSettings.CompanyEnabled;
            model.StreetAddressEnabled = _customerSettings.StreetAddressEnabled;
            model.StreetAddress2Enabled = _customerSettings.StreetAddress2Enabled;
            model.ZipPostalCodeEnabled = _customerSettings.ZipPostalCodeEnabled;
            model.CityEnabled = _customerSettings.CityEnabled;
            model.CountryEnabled = _customerSettings.CountryEnabled;
            model.StateProvinceEnabled = _customerSettings.StateProvinceEnabled;
            model.PhoneEnabled = _customerSettings.PhoneEnabled;
            model.CellPhoneEnabled = _customerSettings.CellPhoneEnabled;
            model.FaxEnabled = _customerSettings.FaxEnabled;

            if (_customerSettings.CountryEnabled)
            {
                model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
                foreach (var c in _countryService.GetAllCountries())
                {
                    model.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString() });
                }

                if (_customerSettings.StateProvinceEnabled)
                {
                    //states
                    var states = _stateProvinceService.GetStateProvincesByCountryId(model.CountryId).ToList();
                    if (states.Count > 0)
                    {
                        foreach (var s in states)
                            model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) });
                    }
                    else
                        model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

                }
            }

            //district list
            model.AvailableDistricts.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Logistics.Common.Fields.AllDistricts"), Value = "0", Selected = true });
            foreach (var d in _logisticsService.GetAllDistricts())
            {
                model.AvailableDistricts.Add(new SelectListItem() { Text = d.Name, Value = d.Id.ToString() });
            }

            model.BillingAddressId = 0;
            model.ShippingAddressId = 0;

            //default value
            model.Active = true;

            var avaliablePaymentMethods = new List<InvoiceStatus>() { InvoiceStatus.PaidByCheck, InvoiceStatus.PaidByCreditCard };
            model.AvailablePaymentMethods = avaliablePaymentMethods.Select(x => new SelectListItem()
            {
                Text = x.GetLocalizedEnum(_localizationService, _workContext),
                Value = ((int)x).ToString(),
                Selected = InvoiceStatus.PaidByCheck == x
            }).ToList();
            return View(model);
        }

        [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        [FormValueRequired("save", "save-continue")]
        public ActionResult Create(CustomerModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            if (!String.IsNullOrWhiteSpace(model.Email))
            {
                var cust2 = _customerService.GetCustomerByEmail(model.Email);
                if (cust2 != null)
                    ModelState.AddModelError("", "Email is already registered");
            }
            if (!String.IsNullOrWhiteSpace(model.Username) & _customerSettings.UsernamesEnabled)
            {
                var cust2 = _customerService.GetCustomerByUsername(model.Username);
                if (cust2 != null)
                    ModelState.AddModelError("", "Username is already registered");
            }

            //validate customer roles
            var allCustomerRoles = _customerService.GetAllCustomerRoles(true);
            var newCustomerRoles = new List<CustomerRole>();
            foreach (var customerRole in allCustomerRoles)
                if (model.SelectedCustomerRoleIds != null && model.SelectedCustomerRoleIds.Contains(customerRole.Id))
                    newCustomerRoles.Add(customerRole);
            var customerRolesError = ValidateCustomerRoles(newCustomerRoles);
            if (!String.IsNullOrEmpty(customerRolesError))
                ModelState.AddModelError("", customerRolesError);
            bool allowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles);

            //validate authorized districts
            District district = null;
            if (model.LogisticsManagerDistrictId.HasValue && model.LogisticsManagerDistrictId.Value != 0)
            {
                district = _logisticsService.GetDistrictById(model.LogisticsManagerDistrictId.Value);
                if (district == null)
                {
                    ModelState.AddModelError("", "The authorized district is invalid.");
                }
            }

            if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.Courier))
            {
                if (model.CourierDistrictIds == null || model.CourierDistrictIds.Length == 0)
                {
                    ModelState.AddModelError("", "Certain authorized districts are required for the courier role.");
                }
                else
                {
                    if (model.CourierDistrictIds.Any(x => _logisticsService.GetDistrictById(x) == null))
                    {
                        ModelState.AddModelError("", "The authorized district is invalid.");
                    }
                }
            }

            if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.LogisticsManager))
            {
                if (!model.LogisticsManagerDistrictId.HasValue)
                {
                    ModelState.AddModelError("", "The authorized district is required for the courier role.");
                }
            }

            if (ModelState.IsValid)
            {
                if (model.StoreId.HasValue && !_storeService.Exists(model.StoreId.Value))
                {
                    throw new InvalidOperationException(String.Format("The assigned store ID {0} doesn't exist or has been deleted.", model.StoreId));
                }

                var customer = new Customer()
                {
                    CustomerGuid = Guid.NewGuid(),
                    Email = model.Email,
                    Username = model.Username,
                    AdminComment = model.AdminComment,
                    IsTaxExempt = model.IsTaxExempt,
                    TimeZoneId = model.TimeZoneId,
                    Active = model.Active,
                    CreatedOnUtc = DateTime.UtcNow,
                    LastActivityDateUtc = DateTime.UtcNow,
                    StoreId = model.StoreId
                };
                _customerService.InsertCustomer(customer);

                //form fields
                if (_customerSettings.GenderEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Gender, model.Gender);
                _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.FirstName, model.FirstName);
                _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.LastName, model.LastName);
                if (_customerSettings.DateOfBirthEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.DateOfBirth, model.DateOfBirth);
                if (_customerSettings.CompanyEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Company, model.Company);
                if (_customerSettings.StreetAddressEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.StreetAddress, model.StreetAddress);
                if (_customerSettings.StreetAddress2Enabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.StreetAddress2, model.StreetAddress2);
                if (_customerSettings.ZipPostalCodeEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.ZipPostalCode, model.ZipPostalCode);
                if (_customerSettings.CityEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.City, model.City);
                if (_customerSettings.CountryEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CountryId, model.CountryId);
                if (_customerSettings.CountryEnabled && _customerSettings.StateProvinceEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.StateProvinceId, model.StateProvinceId);
                if (_customerSettings.PhoneEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Phone, model.Phone);
                if (_customerSettings.CellPhoneEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CellPhone, model.CellPhone);
                if (_customerSettings.FaxEnabled)
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Fax, model.Fax);

                _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.PreferredPaymentMethodId, model.PaymentMethodId);

                //Create addresses
                var defaultAddress = new Address()
                {
                    FirstName = customer.GetAttribute<string>(SystemCustomerAttributeNames.FirstName),
                    LastName = customer.GetAttribute<string>(SystemCustomerAttributeNames.LastName),
                    Email = customer.Email,
                    Company = customer.GetAttribute<string>(SystemCustomerAttributeNames.Company),
                    CountryId = customer.GetAttribute<int>(SystemCustomerAttributeNames.CountryId) > 0 ?
                        (int?)customer.GetAttribute<int>(SystemCustomerAttributeNames.CountryId) : null,
                    StateProvinceId = customer.GetAttribute<int>(SystemCustomerAttributeNames.StateProvinceId) > 0 ?
                        (int?)customer.GetAttribute<int>(SystemCustomerAttributeNames.StateProvinceId) : null,
                    City = customer.GetAttribute<string>(SystemCustomerAttributeNames.City),
                    Address1 = customer.GetAttribute<string>(SystemCustomerAttributeNames.StreetAddress),
                    Address2 = customer.GetAttribute<string>(SystemCustomerAttributeNames.StreetAddress2),
                    ZipPostalCode = customer.GetAttribute<string>(SystemCustomerAttributeNames.ZipPostalCode),
                    PhoneNumber = customer.GetAttribute<string>(SystemCustomerAttributeNames.Phone),
                    CellPhoneNumber = customer.GetAttribute<string>(SystemCustomerAttributeNames.CellPhone),
                    FaxNumber = customer.GetAttribute<string>(SystemCustomerAttributeNames.Fax),
                    CreatedOnUtc = customer.CreatedOnUtc
                };

                if (!_addressService.IsAddressValid(defaultAddress))
                {
                    ErrorNotification("The address is not valid.");
                }
                else
                {
					customer.Addresses = new List<Address>();
                    customer.Addresses.Add(defaultAddress);
                    customer.BillingAddress = defaultAddress;
                    customer.ShippingAddress = defaultAddress;
                    _customerService.UpdateCustomer(customer);
                }

                //password
                if (!String.IsNullOrWhiteSpace(model.Password))
                {
                    var changePassRequest = new ChangePasswordRequest(model.Email, false, _customerSettings.DefaultPasswordFormat, model.Password);
                    var changePassResult = _customerRegistrationService.ChangePassword(changePassRequest);
                    if (!changePassResult.Success)
                    {
                        foreach (var changePassError in changePassResult.Errors)
                            ErrorNotification(changePassError);
                    }
                }

                //customer roles
                if (allowManagingCustomerRoles)
                {
                    foreach (var customerRole in newCustomerRoles)
                        customer.CustomerRoleMappings.Add(new CustomerRoleMapping()
                        {
                            CustomerId = customer.Id,
                            CustomerRoleId = customerRole.Id,
                            StartDate = DateTime.UtcNow
                        });
                    _customerService.UpdateCustomer(customer);

                    if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.Courier))
                    {
                        var courier = _logisticsService.GetCourierById(customer.Id);
                        if (courier == null && model.CourierDistrictIds != null)
                        {
                            _logisticsService.InsertCourier(customer, model.CourierDistrictIds);
                            courier = _logisticsService.GetCourierById(customer.Id);
                        }

                        _logisticsService.UpdateCourier(courier);
                    }

                    if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.LogisticsManager))
                    {
                        _customerService.SetDistrictId(customer, model.LogisticsManagerDistrictId);
                    }

                    //if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.LogisticsMember))
                    //{
                    //    SetExpressMemberExpireDays(customer, model.ExpressMemberExpireDays);
                    //    _customerActivityService.InsertActivity("ExpressExpireDays",
                    //        "The express member expire days were assigned to {0} (ID={1})",
                    //        model.ExpressMemberExpireDays,
                    //        customer.Id);
                    //}
                }

                //activity log
                _customerActivityService.InsertActivity("AddNewCustomer", _localizationService.GetResource("ActivityLog.AddNewCustomer"), customer.Id);

                SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.Added"));
                return continueEditing ? RedirectToAction("Edit", new { id = customer.Id }) : RedirectToAction("List");
            }

            //If we got this far, something failed, redisplay form
            model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            model.AllowUsersToChangeUsernames = _customerSettings.AllowUsersToChangeUsernames;
            model.AllowCustomersToSetTimeZone = _dateTimeSettings.AllowCustomersToSetTimeZone;
            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == model.TimeZoneId) });
            model.DisplayVatNumber = false;
            //customer roles
            model.AvailableCustomerRoles = _customerService
                .GetAllCustomerRoles(true)
                .Select(cr => cr.ToModel())
                .ToList();
            model.AllowManagingCustomerRoles = allowManagingCustomerRoles;
            //form fields
            model.GenderEnabled = _customerSettings.GenderEnabled;
            model.DateOfBirthEnabled = _customerSettings.DateOfBirthEnabled;
            model.CompanyEnabled = _customerSettings.CompanyEnabled;
            model.StreetAddressEnabled = _customerSettings.StreetAddressEnabled;
            model.StreetAddress2Enabled = _customerSettings.StreetAddress2Enabled;
            model.ZipPostalCodeEnabled = _customerSettings.ZipPostalCodeEnabled;
            model.CityEnabled = _customerSettings.CityEnabled;
            model.CountryEnabled = _customerSettings.CountryEnabled;
            model.StateProvinceEnabled = _customerSettings.StateProvinceEnabled;
            model.CellPhoneEnabled = _customerSettings.CellPhoneEnabled;
            model.PhoneEnabled = _customerSettings.PhoneEnabled;
            model.FaxEnabled = _customerSettings.FaxEnabled;
            if (_customerSettings.CountryEnabled)
            {
                model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Address.SelectCountry"), Value = "0" });
                foreach (var c in _countryService.GetAllCountries())
                {
                    model.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == model.CountryId) });
                }


                if (_customerSettings.StateProvinceEnabled)
                {
                    //states
                    var states = _stateProvinceService.GetStateProvincesByCountryId(model.CountryId).ToList();
                    if (states.Count > 0)
                    {
                        foreach (var s in states)
                            model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) });
                    }
                    else
                        model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Address.OtherNonUS"), Value = "0" });

                }
            }

            //district list
            model.AvailableDistricts.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Logistics.Common.Fields.AllDistricts"), Value = "0", Selected = true });
            foreach (var d in _logisticsService.GetAllDistricts())
            {
                model.AvailableDistricts.Add(new SelectListItem() { Text = d.Name, Value = d.Id.ToString() });
            }
            return View(model);

        }

        public ActionResult Edit(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(id);
            if (customer == null || customer.Deleted)
                //No customer found with the specified id
                return RedirectToAction("List");

            var model = new CustomerModel();
            model.Id = customer.Id;
            model.Email = customer.Email;
            model.Username = customer.Username;
            model.AdminComment = customer.AdminComment;
            model.IsTaxExempt = customer.IsTaxExempt;
            model.Active = customer.Active;
            model.AffiliateId = customer.AffiliateId;
            model.TimeZoneId = customer.TimeZoneId;
            model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            model.AllowUsersToChangeUsernames = _customerSettings.AllowUsersToChangeUsernames;
            model.AllowCustomersToSetTimeZone = _dateTimeSettings.AllowCustomersToSetTimeZone;
            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == customer.TimeZoneId) });
            model.DisplayVatNumber = _taxSettings.EuVatEnabled;
            model.VatNumber = customer.VatNumber;
            model.VatNumberStatusNote = customer.VatNumberStatus.GetLocalizedEnum(_localizationService, _workContext);
            model.CreatedOn = _dateTimeHelper.ConvertToUserTime(customer.CreatedOnUtc, DateTimeKind.Utc);
            model.LastActivityDate = _dateTimeHelper.ConvertToUserTime(customer.LastActivityDateUtc, DateTimeKind.Utc);
            model.LastIpAddress = customer.LastIpAddress;
            model.LastVisitedPage = customer.GetAttribute<string>(SystemCustomerAttributeNames.LastVisitedPage);

            //form fields
            model.FirstName = customer.GetAttribute<string>(SystemCustomerAttributeNames.FirstName);
            model.LastName = customer.GetAttribute<string>(SystemCustomerAttributeNames.LastName);
            model.Gender = customer.GetAttribute<string>(SystemCustomerAttributeNames.Gender);
            model.DateOfBirth = customer.GetAttribute<DateTime?>(SystemCustomerAttributeNames.DateOfBirth);
            model.Company = customer.GetAttribute<string>(SystemCustomerAttributeNames.Company);
            model.StreetAddress = customer.GetAttribute<string>(SystemCustomerAttributeNames.StreetAddress);
            model.StreetAddress2 = customer.GetAttribute<string>(SystemCustomerAttributeNames.StreetAddress2);
            model.ZipPostalCode = customer.GetAttribute<string>(SystemCustomerAttributeNames.ZipPostalCode);
            model.City = customer.GetAttribute<string>(SystemCustomerAttributeNames.City);
            model.CountryId = customer.GetAttribute<int>(SystemCustomerAttributeNames.CountryId);
            model.StateProvinceId = customer.GetAttribute<int>(SystemCustomerAttributeNames.StateProvinceId);
            model.Phone = customer.GetAttribute<string>(SystemCustomerAttributeNames.Phone);
            model.CellPhone = customer.GetAttribute<string>(SystemCustomerAttributeNames.CellPhone);
            model.Fax = customer.GetAttribute<string>(SystemCustomerAttributeNames.Fax);

            model.GenderEnabled = _customerSettings.GenderEnabled;
            model.DateOfBirthEnabled = _customerSettings.DateOfBirthEnabled;
            model.CompanyEnabled = _customerSettings.CompanyEnabled;
            model.StreetAddressEnabled = _customerSettings.StreetAddressEnabled;
            model.StreetAddress2Enabled = _customerSettings.StreetAddress2Enabled;
            model.ZipPostalCodeEnabled = _customerSettings.ZipPostalCodeEnabled;
            model.CityEnabled = _customerSettings.CityEnabled;
            model.CountryEnabled = _customerSettings.CountryEnabled;
            model.StateProvinceEnabled = _customerSettings.StateProvinceEnabled;
            model.PhoneEnabled = _customerSettings.PhoneEnabled;
            model.CellPhoneEnabled = _customerSettings.CellPhoneEnabled;
            model.FaxEnabled = _customerSettings.FaxEnabled;

            //countries and states
            if (_customerSettings.CountryEnabled)
            {
                model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
                foreach (var c in _countryService.GetAllCountries())
                {
                    model.AvailableCountries.Add(new SelectListItem()
                    {
                        Text = c.Name,
                        Value = c.Id.ToString(),
                        Selected = c.Id == model.CountryId
                    });
                }

                if (_customerSettings.StateProvinceEnabled)
                {
                    //states
                    var states = _stateProvinceService.GetStateProvincesByCountryId(model.CountryId).ToList();
                    if (states.Count > 0)
                    {
                        foreach (var s in states)
                            model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) });
                    }
                    else
                        model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

                }
            }

            //customer roles
            model.AvailableCustomerRoles = _customerService
                .GetAllCustomerRoles(true)
                .Select(cr => cr.ToModel())
                .ToList();
            model.SelectedCustomerRoleIds = customer.CustomerRoleMappings.Select(cr => cr.CustomerRoleId).ToArray();
            model.AllowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles);

            // member expire days
            //if (customer.CustomerRoles.Any(cr => cr.SystemName.Equals(SystemCustomerRoleNames.LogisticsMember)))
            //{
            //    var expireDays = GetExpressMemberExpireDays(customer);
            //    model.ExpressMemberExpireDays = 0;
            //    if (expireDays > 0)
            //    {
            //        model.ExpressMemberExpireDays = expireDays;
            //    }
            //}
            //address
            if (customer.ShippingAddress != null)
            {
                model.ShippingAddressId = customer.ShippingAddress.Id;
            }
            if (customer.BillingAddress != null)
            {
                model.BillingAddressId = customer.BillingAddress.Id;
            }


            //reward points gistory
            model.DisplayRewardPointsHistory = _rewardPointsSettings.Enabled;
            model.AddRewardPointsValue = 0;
            model.AddRewardPointsMessage = "Some comment here...";
            //external authentication records
            model.AssociatedExternalAuthRecords = GetAssociatedExternalAuthRecords(customer);

            //store list
            model.StoreId = customer.StoreId;
            if (model.StoreId != null)
            {
                var storeId = model.StoreId ?? 0;
                var store = _storeService.GetStoreById(storeId);
                if (store != null)
                    model.StoreName = store.Name;
            }
            //district list
            model.LogisticsManagerDistrictId = _customerService.GetDistrictId(customer);

            if (customer.CustomerRoleMappings.Any(x => x.CustomerRole.SystemName == SystemCustomerRoleNames.Courier))
            {
                model.CourierDistrictIds = _logisticsService.GetCourierById(customer.Id).DistrictIds;
            }
            var districtId = model.LogisticsManagerDistrictId;

            model.AvailableDistricts.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Logistics.Common.Fields.AllDistricts"), Value = "0", Selected = (0 == districtId) });
            foreach (var d in _logisticsService.GetAllDistricts())
            {
                model.AvailableDistricts.Add(new SelectListItem() { Text = d.Name, Value = d.Id.ToString(), Selected = (d.Id == districtId) });
            }

            model.PaymentMethodId = customer.GetAttribute<int>(SystemCustomerAttributeNames.PreferredPaymentMethodId);

            var avaliablePaymentMethods = new List<InvoiceStatus>() { InvoiceStatus.PaidByCheck, InvoiceStatus.PaidByCreditCard };
            model.AvailablePaymentMethods = avaliablePaymentMethods.Select(x => new SelectListItem()
            {
                Text = x.GetLocalizedEnum(_localizationService, _workContext),
                Value = ((int)x).ToString(),
                Selected = model.PaymentMethodId == (int?)x
            }).ToList();
            return View(model);
        }

        [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        [FormValueRequired("save", "save-continue")]
        public ActionResult Edit(CustomerModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.Id);
            if (customer == null || customer.Deleted)
                //No customer found with the specified id
                return RedirectToAction("List");

            //validate customer roles
            var allCustomerRoles = _customerService.GetAllCustomerRoles(true);
            var newCustomerRoles = new List<CustomerRole>();
            foreach (var customerRole in allCustomerRoles)
                if (model.SelectedCustomerRoleIds != null && model.SelectedCustomerRoleIds.Contains(customerRole.Id))
                    newCustomerRoles.Add(customerRole);
            var customerRolesError = ValidateCustomerRoles(newCustomerRoles);
            if (!String.IsNullOrEmpty(customerRolesError))
                ModelState.AddModelError("", customerRolesError);
            bool allowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles);

            //validate authorized districts
            District district = null;
            if (model.LogisticsManagerDistrictId.HasValue && model.LogisticsManagerDistrictId.Value != 0)
            {
                district = _logisticsService.GetDistrictById(model.LogisticsManagerDistrictId.Value);
                if (district == null)
                {
                    ModelState.AddModelError("", "The authorized district is invalid.");
                }
            }

            if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.Courier))
            {
                if (model.CourierDistrictIds == null || model.CourierDistrictIds.Length == 0)
                {
                    ModelState.AddModelError("", "Certain authorized districts are required for the courier role.");
                }
                else
                {
                    if (model.CourierDistrictIds.Any(x => _logisticsService.GetDistrictById(x) == null))
                    {
                        ModelState.AddModelError("", "The authorized district is invalid.");
                    }
                }
            }

            if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.LogisticsManager))
            {
                if (!model.LogisticsManagerDistrictId.HasValue)
                {
                    ModelState.AddModelError("", "The authorized district is required for the logistics manager role.");
                }
            }

            if (ModelState.IsValid)
            {
                try
                {
                    string prevVatNumber = customer.VatNumber;

                    customer.AdminComment = model.AdminComment;
                    customer.IsTaxExempt = model.IsTaxExempt;
                    customer.TimeZoneId = model.TimeZoneId;
                    customer.Active = model.Active;

                    if (model.StoreId.HasValue && !_storeService.Exists(model.StoreId.Value))
                    {
                        throw new InvalidOperationException(String.Format("The assigned store ID {0} doesn't exist or has been deleted.", model.StoreId));
                    }
                    else
                    {
                        customer.StoreId = model.StoreId;
                    }

                    //email
                    if (!String.IsNullOrWhiteSpace(model.Email))
                    {
                        _customerRegistrationService.SetEmail(customer, model.Email);
                    }
                    else
                    {
                        customer.Email = model.Email;
                    }

                    //username
                    if (_customerSettings.UsernamesEnabled && _customerSettings.AllowUsersToChangeUsernames)
                    {
                        if (!String.IsNullOrWhiteSpace(model.Username))
                        {
                            _customerRegistrationService.SetUsername(customer, model.Username);
                        }
                        else
                        {
                            customer.Username = model.Username;
                        }
                    }

                    //VAT number
                    if (_taxSettings.EuVatEnabled)
                    {
                        customer.VatNumber = model.VatNumber;
                        //set VAT number status
                        if (!String.IsNullOrEmpty(customer.VatNumber))
                        {
                            if (!customer.VatNumber.Equals(prevVatNumber, StringComparison.InvariantCultureIgnoreCase))
                                customer.VatNumberStatus = _taxService.GetVatNumberStatus(customer.VatNumber);
                        }
                        else
                            customer.VatNumberStatus = VatNumberStatus.Empty;
                    }
                    _customerService.UpdateCustomer(customer);

                    //form fields
					if (_customerSettings.GenderEnabled && !string.IsNullOrEmpty(model.Gender))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Gender, model.Gender);
					if(!string.IsNullOrEmpty(model.FirstName))
						_genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.FirstName, model.FirstName);
					if (!string.IsNullOrEmpty(model.LastName))
						_genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.LastName, model.LastName);
                    if (_customerSettings.DateOfBirthEnabled)
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.DateOfBirth, model.DateOfBirth);
                    if (_customerSettings.CompanyEnabled)
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Company, model.Company);
                    if (_customerSettings.StreetAddressEnabled && !string.IsNullOrEmpty(model.StreetAddress))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.StreetAddress, model.StreetAddress);
					if (_customerSettings.StreetAddress2Enabled && !string.IsNullOrEmpty(model.StreetAddress))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.StreetAddress2, model.StreetAddress2);
					if (_customerSettings.ZipPostalCodeEnabled && !string.IsNullOrEmpty(model.StreetAddress))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.ZipPostalCode, model.ZipPostalCode);
					if (_customerSettings.CityEnabled && !string.IsNullOrEmpty(model.City))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.City, model.City);
					if (_customerSettings.CountryEnabled && model.CountryId>0)
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CountryId, model.CountryId);
					if (_customerSettings.CountryEnabled && _customerSettings.StateProvinceEnabled && model.StateProvinceId>0)
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.StateProvinceId, model.StateProvinceId);
                    if (_customerSettings.PhoneEnabled && !string.IsNullOrEmpty(model.Phone))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Phone, model.Phone);
					if (_customerSettings.CellPhoneEnabled && !string.IsNullOrEmpty(model.CellPhone))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CellPhone, model.CellPhone);
					if (_customerSettings.FaxEnabled && !string.IsNullOrEmpty(model.CellPhone))
                        _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Fax, model.Fax);

                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.PreferredPaymentMethodId, model.PaymentMethodId);

                    model.PaymentMethodId = customer.GetAttribute<int>(SystemCustomerAttributeNames.PreferredPaymentMethodId);

                    var avaliablePaymentMethods = new List<InvoiceStatus>() { InvoiceStatus.PaidByCheck, InvoiceStatus.PaidByCreditCard };
                    model.AvailablePaymentMethods = avaliablePaymentMethods.Select(x => new SelectListItem()
                    {
                        Text = x.GetLocalizedEnum(_localizationService, _workContext),
                        Value = ((int)x).ToString(),
                        Selected = model.PaymentMethodId == (int?)x
                    }).ToList();

                    //address
                    if (model.BillingAddressId != 0 &&
                        (customer.BillingAddress == null || model.BillingAddressId != customer.BillingAddress.Id))
                    {
                        var address = customer.Addresses.FirstOrDefault(a => a.Id == model.BillingAddressId);
                        if (address != null)
                        {
                            customer.BillingAddress = address;
                            _customerService.UpdateCustomer(customer);
                        }
                        else
                        {
                            ErrorNotification("The billing address ID is invalid.");
                        }
                    }
                    if (model.ShippingAddressId != 0 &&
                        (customer.ShippingAddress == null || model.ShippingAddressId != customer.ShippingAddress.Id))
                    {
                        var address = customer.Addresses.FirstOrDefault(a => a.Id == model.ShippingAddressId);
                        if (address != null)
                        {
                            customer.ShippingAddress = address;
                            _customerService.UpdateCustomer(customer);
                        }
                        else
                        {
                            ErrorNotification("The shipping address ID is invalid.");
                        }
                    }

                    //customer roles
                    if (allowManagingCustomerRoles)
                    {
                        foreach (var customerRole in allCustomerRoles)
                        {
                            if (model.SelectedCustomerRoleIds != null && model.SelectedCustomerRoleIds.Contains(customerRole.Id))
                            {
                                //new role
                                if (customer.CustomerRoleMappings.Where(cr => cr.CustomerRoleId == customerRole.Id).Count() == 0)
                                    customer.CustomerRoleMappings.Add(new CustomerRoleMapping()
                                    {
                                        CustomerId = customer.Id,
                                        CustomerRoleId = customerRole.Id,
                                        StartDate = DateTime.UtcNow
                                    });
                            }
                            else
                            {
                                //removed role
                                if (customer.CustomerRoleMappings.Where(cr => cr.CustomerRoleId == customerRole.Id).Count() > 0)
                                    customer.CustomerRoleMappings.Remove(customer.CustomerRoleMappings.First(crm => crm.CustomerRoleId == customerRole.Id));
                            }
                        }
                        _customerService.UpdateCustomer(customer);

                        if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.Courier))
                        {
                            var courier = _logisticsService.GetCourierById(customer.Id);
                            if (courier == null && model.CourierDistrictIds != null)
                            {
                                _logisticsService.InsertCourier(customer, model.CourierDistrictIds);
                            }
                            else if (model.CourierDistrictIds != null)
                            {
                                courier.DistrictIds = model.CourierDistrictIds;
                            }

                            _logisticsService.UpdateCourier(courier);
                        }
                        else if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.LogisticsManager))
                        {
                            _customerService.SetDistrictId(customer, model.LogisticsManagerDistrictId);
                        }
                        else if (newCustomerRoles.Any(cr => cr.SystemName == SystemCustomerRoleNames.CommerceManager))
                        {
                            _customerService.SetDistrictId(customer, model.LogisticsManagerDistrictId);
                        }

                        //if (customer.CustomerRoles.Any(cr => cr.SystemName.Equals(SystemCustomerRoleNames.LogisticsMember)))
                        //{
                        //    var expireDays = GetExpressMemberExpireDays(customer);
                        //    if (model.ExpressMemberExpireDays != expireDays)
                        //    {
                        //        SetExpressMemberExpireDays(customer, model.ExpressMemberExpireDays);
                        //        _customerActivityService.InsertActivity("ExpressExpireDays",
                        //            "The express member expire days were set from {0} to {1} (ID={2})",
                        //            expireDays,
                        //            model.ExpressMemberExpireDays,
                        //            customer.Id);
                        //    }
                        //}
                    }

                    //activity log
                    _customerActivityService.InsertActivity("EditCustomer", _localizationService.GetResource("ActivityLog.EditCustomer"), customer.Id);

                    SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.Updated"));
                    return continueEditing ? RedirectToAction("Edit", customer.Id) : RedirectToAction("List");
                }
                catch (Exception exc)
                {
                    ErrorNotification(exc.Message, false);
                }
            }


            //If we got this far, something failed, redisplay form
            model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            model.AllowUsersToChangeUsernames = _customerSettings.AllowUsersToChangeUsernames;
            model.AllowCustomersToSetTimeZone = _dateTimeSettings.AllowCustomersToSetTimeZone;
            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == model.TimeZoneId) });
            model.DisplayVatNumber = _taxSettings.EuVatEnabled;
            model.VatNumberStatusNote = customer.VatNumberStatus.GetLocalizedEnum(_localizationService, _workContext);
            model.CreatedOn = _dateTimeHelper.ConvertToUserTime(customer.CreatedOnUtc, DateTimeKind.Utc);
            model.LastActivityDate = _dateTimeHelper.ConvertToUserTime(customer.LastActivityDateUtc, DateTimeKind.Utc);
            model.LastIpAddress = model.LastIpAddress;
            model.LastVisitedPage = customer.GetAttribute<string>(SystemCustomerAttributeNames.LastVisitedPage);
            //form fields
            model.GenderEnabled = _customerSettings.GenderEnabled;
            model.DateOfBirthEnabled = _customerSettings.DateOfBirthEnabled;
            model.CompanyEnabled = _customerSettings.CompanyEnabled;
            model.StreetAddressEnabled = _customerSettings.StreetAddressEnabled;
            model.StreetAddress2Enabled = _customerSettings.StreetAddress2Enabled;
            model.ZipPostalCodeEnabled = _customerSettings.ZipPostalCodeEnabled;
            model.CityEnabled = _customerSettings.CityEnabled;
            model.CountryEnabled = _customerSettings.CountryEnabled;
            model.StateProvinceEnabled = _customerSettings.StateProvinceEnabled;
            model.PhoneEnabled = _customerSettings.PhoneEnabled;
            model.CellPhoneEnabled = _customerSettings.CellPhoneEnabled;
            model.FaxEnabled = _customerSettings.FaxEnabled;
            //countries and states
            if (_customerSettings.CountryEnabled)
            {
                model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
                foreach (var c in _countryService.GetAllCountries())
                {
                    model.AvailableCountries.Add(new SelectListItem()
                    {
                        Text = c.Name,
                        Value = c.Id.ToString(),
                        Selected = c.Id == model.CountryId
                    });
                }

                if (_customerSettings.StateProvinceEnabled)
                {
                    //states
                    var states = _stateProvinceService.GetStateProvincesByCountryId(model.CountryId).ToList();
                    if (states.Count > 0)
                    {
                        foreach (var s in states)
                            model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) });
                    }
                    else
                        model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

                }
            }
            //customer roles
            model.AvailableCustomerRoles = _customerService
                .GetAllCustomerRoles(true)
                .Select(cr => cr.ToModel())
                .ToList();
            model.AllowManagingCustomerRoles = allowManagingCustomerRoles;

            if (allowManagingCustomerRoles)
            {
                var districtId = model.LogisticsManagerDistrictId;

                model.AvailableDistricts.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Logistics.Common.Fields.AllDistricts"), Value = "0", Selected = (0 == districtId) });
                foreach (var d in _logisticsService.GetAllDistricts())
                {
                    model.AvailableDistricts.Add(new SelectListItem() { Text = d.Name, Value = d.Id.ToString(), Selected = (d.Id == districtId) });
                }
            }

            //address
            if (customer.ShippingAddress != null)
            {
                model.ShippingAddressId = customer.ShippingAddress.Id;
            }
            if (customer.BillingAddress != null)
            {
                model.BillingAddressId = customer.BillingAddress.Id;
            }

            //reward points gistory
            model.DisplayRewardPointsHistory = _rewardPointsSettings.Enabled;
            model.AddRewardPointsValue = 0;
            model.AddRewardPointsMessage = "Some comment here...";
            //external authentication records
            model.AssociatedExternalAuthRecords = GetAssociatedExternalAuthRecords(customer);
            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("changepassword")]
        public ActionResult ChangePassword(CustomerModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.Id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            if (ModelState.IsValid)
            {
                var changePassRequest = new ChangePasswordRequest(model.Email,
                    false, _customerSettings.DefaultPasswordFormat, model.Password);
                var changePassResult = _customerRegistrationService.ChangePassword(changePassRequest);
                if (changePassResult.Success)
                    SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.PasswordChanged"));
                else
                    foreach (var error in changePassResult.Errors)
                        ErrorNotification(error);
            }

            return RedirectToAction("Edit", customer.Id);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("markVatNumberAsValid")]
        public ActionResult MarkVatNumberAsValid(CustomerModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.Id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            customer.VatNumberStatus = VatNumberStatus.Valid;
            _customerService.UpdateCustomer(customer);

            return RedirectToAction("Edit", customer.Id);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("markVatNumberAsInvalid")]
        public ActionResult MarkVatNumberAsInvalid(CustomerModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.Id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            customer.VatNumberStatus = VatNumberStatus.Invalid;
            _customerService.UpdateCustomer(customer);

            return RedirectToAction("Edit", customer.Id);
        }

        [HttpPost]
        public ActionResult Delete(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            try
            {
                _customerService.DeleteCustomer(customer);

                //activity log
                _customerActivityService.InsertActivity("DeleteCustomer", _localizationService.GetResource("ActivityLog.DeleteCustomer"), customer.Id);

                SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.Deleted"));
                return RedirectToAction("List");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc.Message);
                return RedirectToAction("Edit", new { id = customer.Id });
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("impersonate")]
        public ActionResult Impersonate(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.AllowCustomerImpersonation))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            _genericAttributeService.SaveAttribute<int?>(_workContext.CurrentCustomer,
                SystemCustomerAttributeNames.ImpersonatedCustomerId, customer.Id);

            return RedirectToAction("Index", "Home", new { area = "" });
        }

        public ActionResult SendEmail(CustomerModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.Id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            try
            {
                if (String.IsNullOrWhiteSpace(customer.Email))
                    throw new NopException("Customer email is empty");
                if (!CommonHelper.IsValidEmail(customer.Email))
                    throw new NopException("Customer email is not valid");
                if (String.IsNullOrWhiteSpace(model.SendEmail.Subject))
                    throw new NopException("Email subject is empty");
                if (String.IsNullOrWhiteSpace(model.SendEmail.Body))
                    throw new NopException("Email body is empty");

                var emailAccount = _emailAccountService.GetEmailAccountById(_emailAccountSettings.DefaultEmailAccountId);
                if (emailAccount == null)
                    emailAccount = _emailAccountService.GetAllEmailAccounts().FirstOrDefault();
                if (emailAccount == null)
                    throw new NopException("Email account can't be loaded");

                var email = new QueuedEmail()
                {
                    EmailAccountId = emailAccount.Id,
                    FromName = emailAccount.DisplayName,
                    From = emailAccount.Email,
                    ToName = customer.GetFullName(),
                    To = customer.Email,
                    Subject = model.SendEmail.Subject,
                    Body = model.SendEmail.Body,
                    CreatedOnUtc = DateTime.UtcNow,
                };
                _queuedEmailService.InsertQueuedEmail(email);
                SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.SendEmail.Queued"));
            }
            catch (Exception exc)
            {
                ErrorNotification(exc.Message);
            }

            return RedirectToAction("Edit", new { id = customer.Id });
        }

        public ActionResult SendPm(CustomerModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.Id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            try
            {
                if (!_forumSettings.AllowPrivateMessages)
                    throw new NopException("Private messages are disabled");
                if (customer.IsGuest())
                    throw new NopException("Customer should be registered");
                if (String.IsNullOrWhiteSpace(model.SendPm.Subject))
                    throw new NopException("PM subject is empty");
                if (String.IsNullOrWhiteSpace(model.SendPm.Message))
                    throw new NopException("PM message is empty");


                var privateMessage = new PrivateMessage
                {
                    ToCustomerId = customer.Id,
                    FromCustomerId = _workContext.CurrentCustomer.Id,
                    Subject = model.SendPm.Subject,
                    Text = model.SendPm.Message,
                    IsDeletedByAuthor = false,
                    IsDeletedByRecipient = false,
                    IsRead = false,
                    CreatedOnUtc = DateTime.UtcNow
                };

                _forumService.InsertPrivateMessage(privateMessage);
                SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.SendPM.Sent"));
            }
            catch (Exception exc)
            {
                ErrorNotification(exc.Message);
            }

            return RedirectToAction("Edit", new { id = customer.Id });
        }

        #endregion

        #region Reward points history

        [GridAction]
        public ActionResult RewardPointsHistorySelect(int customerId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                throw new ArgumentException("No customer found with the specified id");

            var model = new List<CustomerModel.RewardPointsHistoryModel>();
            foreach (var rph in customer.RewardPointsHistory.OrderByDescending(rph => rph.CreatedOnUtc).ThenByDescending(rph => rph.Id))
            {
                model.Add(new CustomerModel.RewardPointsHistoryModel()
                    {
                        Points = rph.Points,
                        PointsBalance = rph.PointsBalance,
                        Message = rph.Message,
                        CreatedOn = _dateTimeHelper.ConvertToUserTime(rph.CreatedOnUtc, DateTimeKind.Utc)
                    });
            }
            var gridModel = new GridModel<CustomerModel.RewardPointsHistoryModel>
            {
                Data = model,
                Total = model.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [ValidateInput(false)]
        public ActionResult RewardPointsHistoryAdd(int customerId, int addRewardPointsValue, string addRewardPointsMessage)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                return Json(new { Result = false }, JsonRequestBehavior.AllowGet);

            customer.AddRewardPointsHistoryEntry(addRewardPointsValue, addRewardPointsMessage);
            _customerService.UpdateCustomer(customer);

            return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
        }

        #endregion

        #region Addresses

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult AddressesSelect(int customerId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                throw new ArgumentException("No customer found with the specified id", "customerId");

            var addresses = customer.Addresses.OrderByDescending(a => a.CreatedOnUtc).ThenByDescending(a => a.Id).ToList();
            var addressesModelList = addresses.Select(x =>
                                    {
                                        var model = x.ToModel();
                                        var addressHtmlSb = new StringBuilder("<div>");
                                        if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(model.Company))
                                            addressHtmlSb.AppendFormat("{0}<br />", Server.HtmlEncode(model.Company));
                                        if (_addressSettings.StreetAddressEnabled && !String.IsNullOrEmpty(model.Address1))
                                            addressHtmlSb.AppendFormat("{0}<br />", Server.HtmlEncode(model.Address1));
                                        if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(model.Address2))
                                            addressHtmlSb.AppendFormat("{0}<br />", Server.HtmlEncode(model.Address2));
                                        if (_addressSettings.CityEnabled && !String.IsNullOrEmpty(model.City))
                                            addressHtmlSb.AppendFormat("{0},", Server.HtmlEncode(model.City));
                                        if (_addressSettings.StateProvinceEnabled && !String.IsNullOrEmpty(model.StateProvinceName))
                                            addressHtmlSb.AppendFormat("{0},", Server.HtmlEncode(model.StateProvinceName));
                                        if (_addressSettings.ZipPostalCodeEnabled && !String.IsNullOrEmpty(model.ZipPostalCode))
                                            addressHtmlSb.AppendFormat("{0}<br />", Server.HtmlEncode(model.ZipPostalCode));
                                        if (_addressSettings.CountryEnabled && !String.IsNullOrEmpty(model.CountryName))
                                            addressHtmlSb.AppendFormat("{0}", Server.HtmlEncode(model.CountryName));
                                        addressHtmlSb.Append("</div>");
                                        model.AddressHtml = addressHtmlSb.ToString();
                                        return model;
                                    }).ToList();
            var addressesModel = new PagedList<AddressModel>(addressesModelList, command.Page - 1, command.PageSize);

            var gridModel = new GridModel<AddressModel>
            {
                Data = addressesModel,
                Total = addressesModel.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [GridAction]
        public ActionResult AddressDelete(int customerId, int addressId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                throw new ArgumentException("No customer found with the specified id", "customerId");

            var address = customer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
            customer.RemoveAddress(address);
            _customerService.UpdateCustomer(customer);
            //we cannot delete record in database
            //_addressService.DeleteAddress(address);

            return AddressesSelect(customerId, command);
        }

        public ActionResult AddressCreate(int customerId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            var model = new CustomerAddressModel();
            model.Address = new AddressModel();
            model.CustomerId = customerId;
            model.Address.FirstNameEnabled = true;
            model.Address.FirstNameRequired = true;
            model.Address.LastNameEnabled = true;
            model.Address.LastNameRequired = true;
            model.Address.EmailEnabled = true;
            model.Address.EmailRequired = true;
            model.Address.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.Address.CompanyRequired = _addressSettings.CompanyRequired;
            model.Address.CountryEnabled = _addressSettings.CountryEnabled;
            model.Address.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
            model.Address.CityEnabled = _addressSettings.CityEnabled;
            model.Address.CityRequired = _addressSettings.CityRequired;
            model.Address.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.Address.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.Address.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.Address.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.Address.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.Address.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.Address.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.Address.PhoneRequired = _addressSettings.PhoneRequired;
            model.Address.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
            model.Address.CellPhoneRequired = _addressSettings.CellPhoneRequired;
            model.Address.FaxEnabled = _addressSettings.FaxEnabled;
            model.Address.FaxRequired = _addressSettings.FaxRequired;
            //countries
            model.Address.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
            foreach (var c in _countryService.GetAllCountries(true))
                model.Address.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString() });
            model.Address.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

            return View(model);
        }

        [HttpPost]
        public ActionResult AddressCreate(CustomerAddressModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.CustomerId);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            if (ModelState.IsValid)
            {
                var address = model.Address.ToEntity();
                address.CreatedOnUtc = DateTime.UtcNow;
                //some validation
                if (address.CountryId == 0)
                    address.CountryId = null;
                if (address.StateProvinceId == 0)
                    address.StateProvinceId = null;
                customer.Addresses.Add(address);
                _customerService.UpdateCustomer(customer);

                SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.Addresses.Added"));
                return RedirectToAction("AddressEdit", new { addressId = address.Id, customerId = model.CustomerId });
            }

            //If we got this far, something failed, redisplay form
            model.CustomerId = customer.Id;
            //countries
            model.Address.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
            foreach (var c in _countryService.GetAllCountries(true))
                model.Address.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == model.Address.CountryId) });
            //states
            var states = model.Address.CountryId.HasValue ? _stateProvinceService.GetStateProvincesByCountryId(model.Address.CountryId.Value, true).ToList() : new List<StateProvince>();
            if (states.Count > 0)
            {
                foreach (var s in states)
                    model.Address.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.Address.StateProvinceId) });
            }
            else
                model.Address.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });
            return View(model);
        }

        public ActionResult AddressEdit(int addressId, int customerId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(addressId);
            if (address == null)
                //No address found with the specified id
                return RedirectToAction("Edit", new { id = customer.Id });

            var model = new CustomerAddressModel();
            model.CustomerId = customerId;
            model.Address = address.ToModel();
            model.Address.FirstNameEnabled = true;
            model.Address.FirstNameRequired = true;
            model.Address.LastNameEnabled = true;
            model.Address.LastNameRequired = true;
            model.Address.EmailEnabled = true;
            model.Address.EmailRequired = false;
            model.Address.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.Address.CompanyRequired = _addressSettings.CompanyRequired;
            model.Address.CountryEnabled = _addressSettings.CountryEnabled;
            model.Address.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
            model.Address.CityEnabled = _addressSettings.CityEnabled;
            model.Address.CityRequired = _addressSettings.CityRequired;
            model.Address.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.Address.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.Address.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.Address.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.Address.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.Address.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.Address.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.Address.PhoneRequired = _addressSettings.PhoneRequired;
            model.Address.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
            model.Address.CellPhoneRequired = _addressSettings.CellPhoneRequired;
            model.Address.FaxEnabled = _addressSettings.FaxEnabled;
            model.Address.FaxRequired = _addressSettings.FaxRequired;
            //countries
            model.Address.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
            foreach (var c in _countryService.GetAllCountries(true))
                model.Address.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == address.CountryId) });
            //states
            var states = address.Country != null ? _stateProvinceService.GetStateProvincesByCountryId(address.Country.Id, true).ToList() : new List<StateProvince>();
            if (states.Count > 0)
            {
                foreach (var s in states)
                    model.Address.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == address.StateProvinceId) });
            }
            else
                model.Address.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

            return View(model);
        }

        [HttpPost]
        public ActionResult AddressEdit(CustomerAddressModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.CustomerId);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(model.Address.Id);
            if (address == null)
                //No address found with the specified id
                return RedirectToAction("Edit", new { id = customer.Id });

            if (ModelState.IsValid)
            {
                address = model.Address.ToEntity(address);
                _addressService.UpdateAddress(address);

                SuccessNotification(_localizationService.GetResource("Admin.Customers.Customers.Addresses.Updated"));
                return RedirectToAction("AddressEdit", new { addressId = model.Address.Id, customerId = model.CustomerId });
            }

            //If we got this far, something failed, redisplay form
            model.CustomerId = customer.Id;
            model.Address = address.ToModel();
            model.Address.FirstNameEnabled = true;
            model.Address.FirstNameRequired = true;
            model.Address.LastNameEnabled = true;
            model.Address.LastNameRequired = true;
            model.Address.EmailEnabled = true;
            model.Address.EmailRequired = false;
            model.Address.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.Address.CompanyRequired = _addressSettings.CompanyRequired;
            model.Address.CountryEnabled = _addressSettings.CountryEnabled;
            model.Address.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
            model.Address.CityEnabled = _addressSettings.CityEnabled;
            model.Address.CityRequired = _addressSettings.CityRequired;
            model.Address.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.Address.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.Address.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.Address.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.Address.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.Address.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.Address.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.Address.PhoneRequired = _addressSettings.PhoneRequired;
            model.Address.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
            model.Address.CellPhoneRequired = _addressSettings.CellPhoneRequired;
            model.Address.FaxEnabled = _addressSettings.FaxEnabled;
            model.Address.FaxRequired = _addressSettings.FaxRequired;
            //countries
            model.Address.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
            foreach (var c in _countryService.GetAllCountries(true))
                model.Address.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == address.CountryId) });
            //states
            var states = address.Country != null ? _stateProvinceService.GetStateProvincesByCountryId(address.Country.Id, true).ToList() : new List<StateProvince>();
            if (states.Count > 0)
            {
                foreach (var s in states)
                    model.Address.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == address.StateProvinceId) });
            }
            else
                model.Address.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

            return View(model);
        }

        #endregion

        #region Orders

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult OrderList(int customerId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var orders = _orderService.GetOrdersByCustomerId(customerId, 1, null, command.Page - 1, command.PageSize);

            var model = new GridModel<CustomerModel.OrderModel>
            {
                Data = orders.OrderBy(x => x.CreatedOnUtc)
                    .Select(order =>
                    {
                        var orderModel = new CustomerModel.OrderModel();
                        orderModel.Id = order.Id;
                        orderModel.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService, _workContext);
                        orderModel.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext);
                        orderModel.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext);
                        orderModel.OrderTotal = _priceFormatter.FormatPrice(order.OrderTotal, true, false);
                        orderModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc);
                        return orderModel;
                    }),
                Total = orders.TotalCount
            };

            return new JsonResult
            {
                Data = model
            };
        }

        #endregion

        #region Invoices

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult InvoiceList(int customerId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var invoices = _invoiceService.GetInvoicesByCustomerId(customerId);

            var gridModel = new GridModel<CustomerModel.InvoiceModel>
            {
                Data = invoices
                    .PagedForCommand(command)
                    .Select(invoice =>
                        {
                            var invoiceModel = new CustomerModel.InvoiceModel();
                            invoiceModel.Id = invoice.Id;
                            invoiceModel.InvoiceGuid = invoice.InvoiceGuid;
                            invoiceModel.InvoiceNumber = invoice.InvoiceNumber;
                            invoiceModel.CustomerId = invoice.CustomerId;
                            invoiceModel.CustomerEmail = invoice.Customer.Email;
                            invoiceModel.BillingEmail = invoice.Customer.BillingAddress.Email;
                            invoiceModel.Name = invoice.Name;
                            invoiceModel.InvoiceStatus = invoice.InvoiceStatus.GetLocalizedEnum(_localizationService, _workContext);
                            invoiceModel.InvoiceStatusId = invoice.InvoiceStatusId;
                            invoiceModel.Comment = invoice.Comment;
                            invoiceModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(invoice.CreatedOnUtc, DateTimeKind.Utc);
                            invoiceModel.CreatedOnStr = _dateTimeHelper.ConvertToUserTime(invoice.CreatedOnUtc, DateTimeKind.Utc).ToString("g");
                            invoiceModel.SubTotal = invoice.SubTotal;
                            invoiceModel.Discount = invoice.Discount;
                            invoiceModel.Tax = invoice.Tax;
                            invoiceModel.Total = invoice.Total;
                            invoiceModel.DueDate = invoice.DueDate;
                            invoiceModel.TimeZoneId = invoice.TimeZoneId;
                            var district = _logisticsService.GetDistrictById(invoice.DistrictId);
                            invoiceModel.District = district == null ? "Unknown" : district.Name;
                            return invoiceModel;
                        }),
                Total = invoices.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult PdfInvoice(int Id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(Id);

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintInvoiceToPdf(stream, invoice, _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", string.Format("invoice_{0}.pdf", invoice.InvoiceNumber));
        }

        #endregion

        #region Reports

        public ActionResult Reports()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var model = new CustomerReportsModel();
            //customers by number of orders
            model.BestCustomersByNumberOfOrders = new BestCustomersReportModel();
            model.BestCustomersByNumberOfOrders.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.BestCustomersByNumberOfOrders.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestCustomersByNumberOfOrders.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.BestCustomersByNumberOfOrders.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestCustomersByNumberOfOrders.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.BestCustomersByNumberOfOrders.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //customers by order total
            model.BestCustomersByOrderTotal = new BestCustomersReportModel();
            model.BestCustomersByOrderTotal.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.BestCustomersByOrderTotal.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestCustomersByOrderTotal.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.BestCustomersByOrderTotal.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestCustomersByOrderTotal.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.BestCustomersByOrderTotal.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //customer orders
            model.CustomerOrders = new OrdersReportModel();
            model.CustomerOrders.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.CustomerOrders.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.CustomerOrders.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.CustomerOrders.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //customer birthday
            model.CustomerBirthdays = new CustomerBirthdayModel();
            model.CustomerBirthdays.SearchCustomerRoleIds = new[] { _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered).Id };
            model.CustomerBirthdays.AvailableCustomerRoles = _customerService.GetAllCustomerRoles(true).Select(cr => cr.ToModel()).ToList();

            return View(model);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ReportBestCustomersByOrderTotalList(GridCommand command, BestCustomersReportModel model)
        {
            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;
            ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;


            var items = _customerReportService.GetBestCustomersReport(startDateValue, endDateValue,
                orderStatus, paymentStatus, shippingStatus, 1, 0, 20);
            var gridModel = new GridModel<BestCustomerReportLineModel>
            {
                Data = items.Select(x =>
                {
                    var m = new BestCustomerReportLineModel()
                    {
                        CustomerId = x.CustomerId,
                        OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false),
                        OrderCount = x.OrderCount,
                    };
                    var customer = _customerService.GetCustomerById(x.CustomerId);
                    if (customer != null)
                    {
                        m.CustomerName = customer.IsGuest()
                                             ? _localizationService.GetResource("Admin.Customers.Guest")
                                             : customer.Email;
                    }
                    return m;
                }),
                Total = items.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult ReportBestCustomersByNumberOfOrdersList(GridCommand command, BestCustomersReportModel model)
        {
            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;
            ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;

            var items = _customerReportService.GetBestCustomersReport(startDateValue, endDateValue,
                orderStatus, paymentStatus, shippingStatus, 2, command.Page - 1, command.PageSize);

            var gridModel = new GridModel<BestCustomerReportLineModel>
            {
                Data = items.Select(x =>
                {
                    var m = new BestCustomerReportLineModel()
                    {
                        CustomerId = x.CustomerId,
                        OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false),
                        OrderCount = x.OrderCount,
                    };
                    var customer = _customerService.GetCustomerById(x.CustomerId);
                    if (customer != null)
                    {
                        m.CustomerName = customer.IsGuest()
                                             ? _localizationService.GetResource("Admin.Customers.Guest")
                                             : customer.Email;
                    }
                    return m;
                }),
                Total = items.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [ChildActionOnly]
        public ActionResult ReportRegisteredCustomers()
        {
            var model = GetReportRegisteredCustomersModel();
            return PartialView(model);
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult ReportRegisteredCustomersList(GridCommand command)
        {
            var model = GetReportRegisteredCustomersModel();
            var gridModel = new GridModel<RegisteredCustomerReportLineModel>
            {
                Data = model,
                Total = model.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ReportCustomerOrdersList(GridCommand command, OrdersReportModel model)
        {
            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;

            var items = _customerReportService.GetCustomerOrdersReport(startDateValue, endDateValue,
                orderStatus, paymentStatus, command.Page - 1, command.PageSize);
            var gridModel = new GridModel<OrderReportLineModel>
            {
                Data = items.Select(x =>
                {
                    var m = new OrderReportLineModel()
                    {
                        OrderId = x.OrderId,
                        CustomerId = x.CustomerId,
                        CustomerEmail = x.CustomerEmail,
                        AffiliateId = x.AffiliateId,
                        OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false),
                        OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext),
                        PaymentStatus = x.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext),
                        CreatedOn = x.CreatedOn,
                        CreatedOnStr = x.CreatedOn.ToString("g"),
                    };

                    var customer = _customerService.GetCustomerById(x.CustomerId);
                    if (customer != null)
                    {
                        m.CustomerName = customer.IsGuest()
                                             ? _localizationService.GetResource("Admin.Customers.Guest")
                                             : customer.Email;
                    }

                    return m;
                }),
                Total = items.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ReportCustomerBirthdays(GridCommand command, string searchMonthOfBirth, [ModelBinderAttribute(typeof(CommaSeparatedModelBinder))] int[] searchCustomerRoleIds)
        {
            var month = string.IsNullOrEmpty(searchMonthOfBirth) ? 0 : int.Parse(searchMonthOfBirth);

            var customers = _customerService.GetAllCustomer(0, month, searchCustomerRoleIds, command.Page - 1, command.PageSize);

            var cbs = new List<CustomerModel>();
            foreach (var item in customers)
            {
                var itemModel = PrepareCustomerModelForList(item.BaseCustomer);
                itemModel.DateOfBirth = item.DayOfBirtdhday;
                cbs.Add(itemModel);
            }

            var gridModel = new GridModel<CustomerModel>
            {
                Data = cbs,
                Total = customers.TotalCount
            };

            return new JsonResult
            {
                Data = gridModel
            };
        }

        #endregion

        #region Current shopping cart/ wishlist

        [GridAction(EnableCustomBinding = true)]
        public ActionResult GetCartList(int customerId, int cartTypeId)
        {
            var customer = _customerService.GetCustomerById(customerId);
            var cart = customer.ShoppingCartItems.Where(x => x.ShoppingCartTypeId == cartTypeId).ToList();

            var gridModel = new GridModel<ShoppingCartItemModel>()
            {
                Data = cart.Select(sci =>
                {
                    List<TaxSurcharge> taxRates;
                    var sciModel = new ShoppingCartItemModel()
                    {
                        Id = sci.Id,
                        ProductVariantId = sci.ProductVariantId,
                        Quantity = sci.Quantity,
                        FullProductName = !String.IsNullOrEmpty(sci.ProductVariant.Name) ?
                            string.Format("{0} ({1})", sci.ProductVariant.Product.Name, sci.ProductVariant.Name) :
                            sci.ProductVariant.Product.Name,
                        UnitPrice = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.ProductVariant, _priceCalculationService.GetUnitPrice(sci, true), out taxRates)),
                        Total = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.ProductVariant, _priceCalculationService.GetSubTotal(sci, true), out taxRates)),
                        UpdatedOn = _dateTimeHelper.ConvertToUserTime(sci.UpdatedOnUtc, DateTimeKind.Utc)
                    };
                    return sciModel;
                }),
                Total = cart.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        #endregion

        #region Current express order

        [ChildActionOnly]
        public ActionResult CurrentOrder(int id)
        {
            ViewBag.HasPermission = _permissionService.Authorize(StandardPermissionProvider.ManageOrders);
            if (!ViewBag.HasPermission)
            {
                return View((OrderInformation)null);
            }

            var customer = _customerService.GetCustomerById(id);
            if (customer == null)
                //No customer found with the specified id
                return ViewBag.HasPermission = false;

            var oi = _orderProcessingService.LoadOrderInformation(customer);

            return View(oi);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("placeexpressorder")]
        public ActionResult PlaceOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(id);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            var oi = _orderProcessingService.LoadOrderInformation(customer);

            if (oi == null || !oi.OrderCompleted || oi.OrderPaid)
            {
                ErrorNotification("The order is not completed yet.");
            }
            else
            {
                oi.OrderPaid = true;
                _orderProcessingService.SaveOrderInformation(oi, customer);
                if (_orderProcessingService.PlaceOrder(oi, customer) < 0)
                {
                    ErrorNotification("The order cannot be placed.");
                }
                else
                {
                    SuccessNotification("Successfully placed the order.");
                }
            }
            return RedirectToAction("Edit", new { id = customer.Id });
        }

        #endregion

        #region Activity log

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public JsonResult ListActivityLog(GridCommand command, int customerId)
        {
            var activityLog = _customerActivityService.GetAllActivities(null, null, customerId, 0, command.Page - 1, command.PageSize);
            var gridModel = new GridModel<CustomerModel.ActivityLogModel>
            {
                Data = activityLog.Select(x =>
                {
                    var m = new CustomerModel.ActivityLogModel()
                    {
                        Id = x.Id,
                        ActivityLogTypeName = x.ActivityLogType.Name,
                        Comment = x.Comment,
                        CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc)
                    };
                    return m;

                }),
                Total = activityLog.TotalCount
            };
            return new JsonResult { Data = gridModel }; ;
        }

        #endregion

        #region Export / Import

        public ActionResult ExportExcelAll(CustomerSearchModel SearchModel)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            try
            {

                DateTime? registrationFromValue = (SearchModel.RegistrationFrom == null) ? null
                                : (DateTime?)_dateTimeHelper.ConvertToUtcTime(SearchModel.RegistrationFrom.Value, _dateTimeHelper.CurrentTimeZone);

                DateTime? registrationToValue = (SearchModel.RegistrationTo == null) ? null
                                : (DateTime?)_dateTimeHelper.ConvertToUtcTime(SearchModel.RegistrationTo.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

                var searchDayOfBirth = 0;
                int searchMonthOfBirth = 0;
                if (!String.IsNullOrWhiteSpace(SearchModel.SearchDayOfBirth))
                    searchDayOfBirth = Convert.ToInt32(SearchModel.SearchDayOfBirth);
                if (!String.IsNullOrWhiteSpace(SearchModel.SearchMonthOfBirth))
                    searchMonthOfBirth = Convert.ToInt32(SearchModel.SearchMonthOfBirth);

                IList<Customer> customers = _customerService.GetAllCustomers(registrationFromValue,
                      registrationToValue, SearchModel.SearchCustomerRoleIds.Select(int.Parse).ToArray(), false,
                      SearchModel.SearchEmail, SearchModel.SearchUsername, SearchModel.SearchFirstName, SearchModel.SearchLastName,
                      searchDayOfBirth, searchMonthOfBirth,
                      SearchModel.SearchCompany, SearchModel.SearchPhone, SearchModel.SearchCellPhone, SearchModel.SearchZipPostalCode,
                      false, null, 0, int.MaxValue);

                //Export only registered users
                customers = customers.Where(x => x.IsRegistered()).ToList();

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _exportManager.ExportCustomersToXlsx(stream, customers);
                    bytes = stream.ToArray();
                }
                return File(bytes, "text/xls", "customers.xlsx");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportExcelSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customers = new List<Customer>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                customers.AddRange(_customerService.GetCustomersByIds(ids));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _exportManager.ExportCustomersToXlsx(stream, customers);
                bytes = stream.ToArray();
            }
            return File(bytes, "text/xls", "customers.xlsx");
        }

        public ActionResult ExportXmlAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            try
            {
                var customers = _customerService.GetAllCustomers(null, null, null, false, null,
                    null, null, null, 0, 0, null, null, null, null,
                    false, null, 0, int.MaxValue);

                var xml = _exportManager.ExportCustomersToXml(customers);
                return new XmlDownloadResult(xml, "customers.xml");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportXmlSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customers = new List<Customer>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                customers.AddRange(_customerService.GetCustomersByIds(ids));
            }

            var xml = _exportManager.ExportCustomersToXml(customers);
            return new XmlDownloadResult(xml, "customers.xml");
        }

        [HttpPost]
        public ActionResult BulkPlaceExpressOrder(int customerId, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(customerId);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            if (!_orderProcessingService.IsPaymentExempt(customer))
            {
                ErrorNotification(_localizationService.GetResource("Admin.Customers.CustomerMustPay"));
                return RedirectToAction("Edit", new { Id = customer.Id });
            }

            if (customer.ShippingAddress == null)
            {
                ErrorNotification(_localizationService.GetResource("Admin.Customers.ShippingAddressRequired"));
                return RedirectToAction("Edit", new { Id = customer.Id });
            }

            //set page timeout to 5 minutes
            this.Server.ScriptTimeout = 300;

            try
            {
                var file = Request.Files["importxlsxfile"];
                if (file != null && file.ContentLength > 0 && (file.FileName.Contains(".xls") || file.FileName.Contains(".xlsx")))
                {
                    var addresses = _importManager.ImportAddressesFromXlsx(file.InputStream);
                    if (addresses.Count() > _logisticsInformationSettings.MaxBulkOrderAddress)
                    {
                        ErrorNotification(string.Format(_localizationService.GetResource("Admin.Customers.MaximumAddresses"), _logisticsInformationSettings.MaxBulkOrderAddress));
                        return RedirectToAction("Edit", new { id = customer.Id });
                    }

                    var model = new BulkExpressOrderModel();

                    foreach (var addr in addresses)
                    {
                        if (addr != null)
                        {
                            var addressModel = addr.ToModel();
                            addressModel.FirstNameEnabled = true;
                            addressModel.FirstNameRequired = true;
                            addressModel.LastNameEnabled = true;
                            addressModel.LastNameRequired = true;
                            addressModel.EmailEnabled = true;
                            addressModel.EmailRequired = false;
                            addressModel.CompanyEnabled = _addressSettings.CompanyEnabled;
                            addressModel.CompanyRequired = _addressSettings.CompanyRequired;
                            addressModel.CountryEnabled = _addressSettings.CountryEnabled;
                            addressModel.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
                            addressModel.CityEnabled = _addressSettings.CityEnabled;
                            addressModel.CityRequired = _addressSettings.CityRequired;
                            addressModel.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
                            addressModel.StreetAddressRequired = _addressSettings.StreetAddressRequired;
                            addressModel.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
                            addressModel.StreetAddress2Required = _addressSettings.StreetAddress2Required;
                            addressModel.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
                            addressModel.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
                            addressModel.PhoneEnabled = _addressSettings.PhoneEnabled;
                            addressModel.PhoneRequired = _addressSettings.PhoneRequired;
                            addressModel.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
                            addressModel.CellPhoneRequired = _addressSettings.CellPhoneRequired;
                            addressModel.FaxEnabled = _addressSettings.FaxEnabled;
                            addressModel.FaxRequired = _addressSettings.FaxRequired;

                            if (TryValidateModel(addressModel))
                            {
                                //Get default country if the identifier is null
                                int countryId = addressModel.CountryId ?? _countryService.GetAllCountries().First().Id;
                                addressModel.AvailableCountries = _countryService.GetAllCountries().Select(
                                    x => new SelectListItem()
                                    {
                                        Text = x.Name,
                                        Value = x.Id.ToString(),
                                        Selected = countryId == x.Id
                                    }).ToList();

                                var states = _stateProvinceService.GetStateProvincesByCountryId(countryId).ToList();
                                if (states.Count > 0)
                                {
                                    addressModel.AvailableStates = states.Select(
                                        s => new SelectListItem()
                                        {
                                            Text = s.Name,
                                            Value = s.Id.ToString(),
                                            Selected = s.Id == addressModel.StateProvinceId
                                        }).ToList();
                                }

                                addressModel.AvailableTypes = ((AddressType)addressModel.TypeId).ToSelectList(true).ToList();
                                model.Addresses.Add(addressModel);
                            }
                        }
                    }

                    model.CustomerId = customer.Id;
                    model.IsLetterSize = false;
                    model.IsVertical = false;
                    model.IsFragile = false;
                    model.IsDangerous = false;
                    model.NoSignature = false;

                    string lUnit = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).Name;
                    string wUnit = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name;
                    model.DimensionMeasureUnit = lUnit;
                    model.WeightMeasureUnit = wUnit;

                    SuccessNotification(_localizationService.GetResource("Admin.Customers.ImportedAddresses"));
                    return View(model);
                }
                else
                {
                    ErrorNotification(_localizationService.GetResource("Admin.Customers.UploadExcelFile"));
                    return RedirectToAction("Edit", new { id = customer.Id });
                }
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("Edit", new { id = customer.Id });
            }
        }

        [HttpPost]
        public ActionResult BulkPlaceExpressOrderConfirm(BulkExpressOrderModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers))
                return AccessDeniedView();

            var customer = _customerService.GetCustomerById(model.CustomerId);
            if (customer == null)
                //No customer found with the specified id
                return RedirectToAction("List");

            if (customer.ShippingAddress == null)
            {
                ErrorNotification(_localizationService.GetResource("Admin.Customers.ShippingAddressRequired"));
                return RedirectToAction("Edit", new { Id = customer.Id });
            }

            var region = _logisticsService.GetRegionByZipPostalCode(customer.ShippingAddress.ZipPostalCode);
            if (region == null)
            {
                ErrorNotification(_localizationService.GetResource("Admin.Customers.RegionForShippingAddress"));
                return RedirectToAction("Edit", new { Id = customer.Id });
            }
            var regionName = region.GetLocalized(r => r.Name);

            #region validation
            if (!model.IsLetterSize && model.Length.HasValue && model.Width.HasValue && model.Height.HasValue)
            {
                var dim = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);

                var max = MathEx.Round(_measureService.ConvertFromPrimaryMeasureDimension(
                    _logisticsInformationSettings.MaxDimensionSize, dim), 0);
                decimal total = 0;
                total += model.Length.Value + model.Width.Value + model.Height.Value;
                if (total > max)
                {
                    ModelState.AddModelError("", String.Format(_localizationService.GetResource("Admin.Customers.PackageSizeLimitation"),
                        (int)max, dim.Name));
                }
            }
            else if (model.IsLetterSize)
            {
                model.Length = null;
                model.Width = null;
                model.Height = null;
                model.Weight = null;
            }
            else
            {
                ModelState.AddModelError("", string.Format(_localizationService.GetResource("Admin.Customers.DimensionAndWeightRequired")));
            }

            //valid ZipPostalCode again 
            int addressCount = 0;
            model.Addresses.ToList().ForEach(x =>
            {
                addressCount++;
                if (!_logisticsService.IsZipPostalCodeInRegion(region, x.ZipPostalCode))
                {
                    ModelState.AddModelError("", string.Format(_localizationService.GetResource("Admin.Customers.ZipPostalCodeInRegion"), regionName, x.ZipPostalCode, addressCount));
                }
                else if (!(!x.StateProvinceId.HasValue ||
                        x.StateProvinceId.Value == 0 ||
                        x.StateProvinceId.Value == _logisticsService.GetDistrictByZipPostalCode(x.ZipPostalCode).StateProvinceId))
                {
                    ModelState.AddModelError("", string.Format(_localizationService.GetResource("Admin.Customers.ZipPostalCode.NotInStateProvince"), x.ZipPostalCode, addressCount));
                }
            });
            #endregion

            if (ModelState.IsValid)
            {
                try
                {
                    int orderCount = 0;
                    var orderInfo = customer.GetCompanyNameOrFullName();
                    foreach (var addr in model.Addresses)
                    {
                        orderCount++;
                        try
                        {
                            var importAddress = addr.ToEntity();

                            addr.ZipPostalCode = addr.ZipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();
                            var address = customer.Addresses.ToList().FindAddress(
                                addr.FirstName, addr.LastName,
                                addr.PhoneNumber, addr.CellPhoneNumber,
                                addr.Email, addr.FaxNumber, addr.Company,
                                addr.Address1, addr.Address2, addr.City,
                                addr.StateProvinceId, addr.ZipPostalCode, addr.CountryId);

                            if (address == null)
                            {
                                //address is not found. let's create a new one
                                address = importAddress;
                                address.CreatedOnUtc = DateTime.UtcNow;
                                //some validation
                                if (address.CountryId == 0)
                                    address.CountryId = null;
                                if (address.StateProvinceId == 0)
                                    address.StateProvinceId = null;
                                customer.Addresses.Add(address);
                                _customerService.UpdateCustomer(customer);
                            }
                            else if (address.Type != importAddress.Type)
                            {
                                address.Type = importAddress.Type;
                                _addressService.UpdateAddress(address);
                            }

                            var oi = new OrderInformation()
                            {
                                UserAddressId = customer.ShippingAddress.Id
                            };

                            var li = new LocationInformation()
                            {
                                RegionId = 1,
                                PackageNumber = 1,
                                PickupDate = null,
                                PickupTimeWindowId = null,
                                NoSignature = model.NoSignature,
                                IsInterDistrict = IsInterDistrict(customer.ShippingAddress, address), // Update inter-region attribute
                                CanDeliverOnSat = false,
                                CanDeliverOnSun = false,
                                AddressId = address.Id,
                                IsUserAddressPickup = true,
                                AddtionalInstruction = String.IsNullOrEmpty(_logisticsInformationSettings.BulkOrderAdditionalInfo) ?
                                    model.AdditionalInstruction : String.Format(_logisticsInformationSettings.BulkOrderAdditionalInfo, orderCount, model.AdditionalInstruction ?? ""),
                                OrderInfo = orderInfo
                            };

                            oi.Locations.Add(li);

                            var pi = new PackageInformation()
                            {
                                AddtionalAttributes = AddtionalAttributes.None,
                                IsLettersize = model.IsLetterSize,
                                Length = model.Length,
                                Width = model.Width,
                                Height = model.Height,
                                Weight = model.Weight,
                                InsuranceValue = model.InsuranceValue,
                                Comment = model.Comment,
                                DimensionMeasureId = 1,
                                WeightMeasureId = 2
                            };

                            if (li.IsInterDistrict)
                            {
                                pi.AddtionalAttributes |= AddtionalAttributes.InterDistrict;
                            }

                            if (li.NoSignature.Value && li.NoSignature.HasValue)
                            {
                                pi.AddtionalAttributes |= AddtionalAttributes.NoSignature;
                            }

                            if (model.IsVertical)
                            {
                                pi.AddtionalAttributes |= AddtionalAttributes.VerticalHandle;
                            }

                            if (model.IsFragile)
                            {
                                pi.AddtionalAttributes |= AddtionalAttributes.Fragile;
                            }

                            if (model.IsDangerous)
                            {
                                pi.AddtionalAttributes |= AddtionalAttributes.DangerousGood;
                            }

                            if (model.InsuranceValue.HasValue && model.InsuranceValue.Value > _logisticsInformationSettings.InsuranceChargeStep)
                            {
                                pi.AddtionalAttributes |= AddtionalAttributes.Insurance;
                            }

                            li.Packages.Add(pi);

                            oi.OrderCompleted = true;
                            oi.OrderPaid = true;
                            _orderProcessingService.PlaceOrder(oi, customer);
                        }
                        catch (Exception exc)
                        {
                            ErrorNotification(String.Format(_localizationService.GetResource("Admin.Customers.PlaceOrderFail"), addr.Address1, orderCount, exc));
                        }
                    };

                    SuccessNotification(String.Format(_localizationService.GetResource("Admin.Customers.PlaceOrderSuccess"), orderCount));
                    return RedirectToAction("Edit", new { id = customer.Id });
                }
                catch (Exception exc)
                {
                    ErrorNotification(exc);
                }
            }

            //If we got this far, something failed, redisplay form
            string lUnit = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).Name;
            string wUnit = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name;
            model.DimensionMeasureUnit = lUnit;
            model.WeightMeasureUnit = wUnit;

            model.Addresses.ToList().ForEach(x =>
            {
                //states, get default country if the identifier is null
                int countryId = x.CountryId ?? _countryService.GetAllCountries().First().Id;
                x.AvailableCountries = _countryService.GetAllCountries().Select(
                    c => new SelectListItem()
                    {
                        Text = c.Name,
                        Value = c.Id.ToString(),
                        Selected = countryId == c.Id
                    }).ToList();

                var states = _stateProvinceService.GetStateProvincesByCountryId(countryId).ToList();
                if (states.Count > 0)
                {
                    x.AvailableStates = states.Select(
                        s => new SelectListItem()
                    {
                        Text = s.Name,
                        Value = s.Id.ToString(),
                        Selected = s.Id == x.StateProvinceId
                    }).ToList();
                }

                x.AvailableTypes = ((AddressType)x.TypeId).ToSelectList(true).ToList();
            });

            return View("BulkPlaceExpressOrder", model);
        }
        #endregion
    }
}
